#include "Graphix.h"
#include "D3D11.h"
#include "Camera.h"
#include "Model.h"
#include "Light.h"
#include "Shader.h"
#include "Util.h"

namespace TestDX
{

// Constants get value. TODO: Use configuration class
const float Graphix::SCREEN_DEPTH = 1000.0f;
const float Graphix::SCREEN_NEAR = 1.0f;

Graphix::Graphix() :
	_pD3D11(NULL),
	_pCamera(NULL),
	_pModel(NULL),
	_pLight(NULL),
	_pShader(NULL),
	_rotationY(0.0f),
	_rotationZ(0.0f)
{
}

Graphix::~Graphix()
{
	Shutdown();
}

bool Graphix::Init(int width, int height, void * hwnd)
{
	_pD3D11 = new D3D11;

	if(!_pD3D11)
	{
		// @ TODO: Log
		return false;
	}

	if(!_pD3D11->Init((HWND) hwnd, width, height, SCREEN_DEPTH, SCREEN_NEAR))
	{
		// @ TODO: Log
		return false;
	}

	_pCamera = new Camera;
	if(!_pCamera)
	{
		// @ TODO: Log
		return false;
	}
	_pCamera->SetPosition(0.0f, 0.0f, -75.0f);

	_pModel = new Model;
	if(!_pModel)
	{
		// @ TODO: Log
		return false;
	}

	if(!_pModel->Init(_pD3D11->GetDevice(),
					   L"data\\model.txt",
					   L"data\\texture.dds"))
	{
		// @ TODO: Log
		return false;
	}

	_pLight = new Light;
	if(!_pLight)
	{
		// @ TODO: Log
		return false;
	}

	_pLight->SetAmbientColor(0.1f, 0.1f, 0.1f, 0.1f);
	_pLight->SetDiffuseColor(0.8f, 0.8f, 0.8f, 0.8f);
	_pLight->SetDirection(0.0f, 0.0f, 0.8f);
	_pLight->SetSpecularColor(0.8f, 0.8f, 0.8f, 0.8f);
	_pLight->SetSpecularPower(32.0f);

	_pShader = new Shader;
	if(!_pShader)
	{
		// @ TODO: Log
		return false;
	}

	if(!_pShader->Init(_pD3D11->GetDevice(), (HWND) hwnd))
	{
		// @ TODO: Log
		return false;
	}

	return true;
}

void Graphix::Shutdown()
{
	SAFE_DELETE(_pShader);
	SAFE_DELETE(_pLight);
	SAFE_DELETE(_pModel);
	SAFE_DELETE(_pCamera);
	SAFE_DELETE(_pD3D11);
}

bool Graphix::Frame(int mouseDX, int mouseDY)
{
	static const float MOUSE_SENSITIVITY = 0.00075f;
	// @ TODO: Here goes variables updating (if any)
	_rotationY += (float)XM_PI * MOUSE_SENSITIVITY * mouseDX;
	_rotationZ += (float)XM_PI * MOUSE_SENSITIVITY * mouseDY;

	return Render();
}

bool Graphix::Render()
{
	_pD3D11->BeginScene();

	// @ TODO:  Here goes the rendering
	// Compute view matrix
	_pCamera->Render();

	// Get world, view and projection matrices
	XMMATRIX viewMatrix(
		XMLoadFloat4x4(_pCamera->GetViewMatrix()));
	XMMATRIX worldMatrix(
		XMLoadFloat4x4(_pD3D11->GetWorldMatrix()));
	XMMATRIX projectionMatrix(
		XMLoadFloat4x4(_pD3D11->GetProjectionMatrix()));

	// Rotate world matrix
	worldMatrix *= XMMatrixRotationRollPitchYaw(0.0f,
												-(float)XM_PI / 2.0f - _rotationY,
												-(float)XM_PI / 2.0f + _rotationZ);

	// Put model vertex and index buffers on graphics pipeline
	_pModel->Render(_pD3D11->GetDeviceContext());

	// Render using shader
	if(!_pShader->Render(_pD3D11->GetDeviceContext(),
						 _pModel->GetIndexCount(),
						 worldMatrix,
						 viewMatrix,
						 projectionMatrix,
						 _pModel->GetTexture(),
						 _pLight->GetDirection(),
						 _pLight->GetAmbientColor(),
						 _pLight->GetDiffuseColor(),
						 _pCamera->GetPosition(),
						 _pLight->GetSpecularColor(),
						 _pLight->GetSpecularPower()))
	{
		// @ TODO: Log
		return false;
	}					 

	// Present scene
	_pD3D11->EndScene();

	return true;
}

}